Concurrency এবং Socket Programming একসাথে ব্যবহার করা হয় নেটওয়ার্ক অ্যাপ্লিকেশন এবং সার্ভার ডিজাইন করার ক্ষেত্রে, যেখানে একাধিক ক্লায়েন্টের সাথে একই সময়ে সংযোগ স্থাপন এবং ডেটা ট্রান্সমিশন করতে হয়। Concurrency এর মাধ্যমে সার্ভার প্রোগ্রামগুলো একাধিক ক্লায়েন্ট সংযোগ পরিচালনা করতে পারে, যা কার্যকারিতা এবং কর্মক্ষমতা বাড়ায়।
নিচে Concurrency এবং Socket Programming কীভাবে কাজ করে এবং কীভাবে এগুলো একসাথে ব্যবহার করা হয়, তার বিশদ ব্যাখ্যা দেওয়া হলো:
Concurrency নেটওয়ার্ক প্রোগ্রামিংয়ের ক্ষেত্রে একটি অপরিহার্য উপাদান, যা সার্ভারকে একাধিক ক্লায়েন্ট সংযোগ একসাথে পরিচালনা করতে সহায়তা করে। এর মাধ্যমে একই সময়ে একাধিক প্রক্রিয়া বা থ্রেড চালানো যায়, যা সার্ভারের কার্যকারিতা উন্নত করে।
Concurrency সাধারণত তিনটি প্রধান পদ্ধতিতে অর্জন করা হয়:
নিচে একটি TCP সার্ভারের উদাহরণ দেওয়া হলো, যেখানে Multithreading ব্যবহার করে প্রতিটি ক্লায়েন্ট সংযোগ পরিচালনা করা হয়েছে:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
void *handle_client(void *client_socket) {
int sock = *(int*)client_socket;
char buffer[BUFFER_SIZE];
int n;
// ক্লায়েন্ট থেকে ডেটা গ্রহণ করা
while ((n = read(sock, buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("Client: %s\n", buffer);
// ক্লায়েন্টে ডেটা পাঠানো
write(sock, buffer, strlen(buffer));
}
// ক্লায়েন্টের সাথে সংযোগ বন্ধ করা
close(sock);
free(client_socket);
return NULL;
}
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// 1. Socket তৈরি করা
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. Address Structure সেটআপ করা
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 3. Binding করা
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 4. Listening করা
if (listen(server_fd, 5) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server is listening on port %d\n", PORT);
// 5. ক্লায়েন্ট সংযোগ গ্রহণ করা
while ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) >= 0) {
printf("Client connected\n");
// প্রতিটি ক্লায়েন্টের জন্য নতুন থ্রেড তৈরি করা
pthread_t thread_id;
int *new_sock = malloc(sizeof(int));
*new_sock = client_fd;
if (pthread_create(&thread_id, NULL, handle_client, (void*)new_sock) != 0) {
perror("Thread creation failed");
close(client_fd);
}
// থ্রেড detach করা, যাতে সম্পন্ন হলে এটি নিজে থেকেই রিসোর্স মুক্ত করতে পারে
pthread_detach(thread_id);
}
// 6. Server Socket বন্ধ করা
close(server_fd);
return 0;
}
socket()
ফাংশন ব্যবহার করে একটি TCP Socket তৈরি করা হয়েছে।struct sockaddr_in
ব্যবহার করে সার্ভারের IP Address এবং Port Number সেট করা হয়েছে।bind()
এবং listen()
ফাংশন ব্যবহার করে সার্ভার Socket-কে ইনকামিং সংযোগ গ্রহণের জন্য প্রস্তুত করা হয়েছে।pthread_create()
ফাংশন ব্যবহার করে প্রতিটি ক্লায়েন্ট সংযোগের জন্য একটি নতুন থ্রেড তৈরি করা হয়েছে। এই থ্রেডগুলি handle_client()
ফাংশনের মাধ্যমে প্রতিটি সংযোগ পরিচালনা করে।pthread_detach()
ব্যবহার করে থ্রেডগুলোকে detach করা হয়েছে, যাতে থ্রেডগুলো সম্পন্ন হলে তাদের রিসোর্স নিজে থেকে মুক্ত হয়।read()
এবং write()
ফাংশন ব্যবহার করে ক্লায়েন্টের সাথে ডেটা আদান-প্রদান করা হয়েছে।fork()
ফাংশন ব্যবহার করে সম্পন্ন করা হয়। যদিও এটি কার্যকরী, তবে এটি রিসোর্স ব্যবহারের দিক থেকে ব্যয়বহুল হতে পারে।select()
, poll()
, বা epoll()
ব্যবহার করে একাধিক সংযোগ পরিচালনা করা যেতে পারে।Concurrency হলো একাধিক কাজ একই সময়ে চালানোর ধারণা। এটি একটি প্রোগ্রামে বা সিস্টেমে একাধিক কাজ বা প্রক্রিয়া একসাথে সম্পন্ন করার জন্য ব্যবহৃত হয়। Concurrency-এর মাধ্যমে বিভিন্ন কাজ (যেমন ফাংশন কল, প্রক্রিয়া, থ্রেড) একসাথে চালানো হয়, তবে এরা একে অপরের থেকে স্বাধীনভাবে কাজ করে এবং কখনো কখনো একটি কাজ সম্পন্ন হবার আগেই অন্যটি শুরু হয়।
Concurrency মূলত একাধিক প্রক্রিয়া বা থ্রেড একসাথে চালানোর জন্য ব্যবহৃত হয় এবং এটি একটি সিস্টেমের কার্যকারিতা ও কর্মক্ষমতা বৃদ্ধি করতে সাহায্য করে।
Concurrency ব্যবহার করার প্রয়োজনীয়তা এবং এর সুবিধাসমূহ:
কর্মক্ষমতা বৃদ্ধি:
দ্রুত প্রতিক্রিয়া নিশ্চিত করা:
রিসোর্স ব্যবহারের দক্ষতা বাড়ানো:
ব্যাকগ্রাউন্ড প্রসেসিং:
মাল্টিটাস্কিং:
ডেডলক এবং রেস কন্ডিশন মোকাবিলা:
সিস্টেম স্কেলেবিলিটি:
নিচে একটি সাধারণ উদাহরণ দেখানো হলো যেখানে Multithreading ব্যবহার করে Concurrency নিশ্চিত করা হয়েছে:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* print_numbers(void* arg) {
int n = *(int*)arg;
for (int i = 1; i <= n; i++) {
printf("Number: %d\n", i);
sleep(1); // প্রতিটি সংখ্যার পর ১ সেকেন্ড অপেক্ষা করা
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
int n1 = 5, n2 = 3;
// দুটি থ্রেড তৈরি করা
pthread_create(&thread1, NULL, print_numbers, &n1);
pthread_create(&thread2, NULL, print_numbers, &n2);
// প্রধান থ্রেড থ্রেডগুলো শেষ হওয়া পর্যন্ত অপেক্ষা করছে
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("All threads have completed.\n");
return 0;
}
Multi-threading এবং Forking দুটি প্রধান কৌশল, যা Concurrency অর্জনের জন্য ব্যবহৃত হয়। এদের মাধ্যমে নেটওয়ার্ক প্রোগ্রামিং, সার্ভার ডিজাইন, এবং অন্যান্য অ্যাপ্লিকেশনগুলোর ক্ষেত্রে একাধিক কাজ একই সময়ে চালানো যায়। তবে এদের ব্যবহারের পদ্ধতি, কার্যকারিতা, এবং রিসোর্স ব্যবহারের দিক থেকে কিছু পার্থক্য রয়েছে। নিচে Multi-threading এবং Forking এর বিস্তারিত ব্যাখ্যা এবং উদাহরণ দেওয়া হলো।
Multi-threading হলো একটি প্রক্রিয়ার মধ্যে একাধিক থ্রেড তৈরি করা, যেখানে প্রতিটি থ্রেড একটি নির্দিষ্ট কাজ সম্পন্ন করে। এটি Concurrency অর্জনের একটি পদ্ধতি, যেখানে একটি প্রক্রিয়া একাধিক কাজ একসাথে সম্পন্ন করে।
Shared Memory:
কম রিসোর্স ব্যবহৃত হয়:
দ্রুত প্রসেসিং:
Thread Synchronization:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* print_message(void* message) {
char* msg = (char*)message;
for (int i = 0; i < 5; i++) {
printf("%s\n", msg);
sleep(1);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
// দুটি থ্রেড তৈরি করা
pthread_create(&thread1, NULL, print_message, "Thread 1: Hello");
pthread_create(&thread2, NULL, print_message, "Thread 2: World");
// থ্রেডগুলোর কাজ সম্পন্ন হওয়া পর্যন্ত অপেক্ষা করা
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("All threads have completed.\n");
return 0;
}
Forking হলো একটি নতুন প্রক্রিয়া তৈরি করার পদ্ধতি, যেখানে মূল (parent) প্রক্রিয়া থেকে একটি নতুন (child) প্রক্রিয়া তৈরি করা হয়। এটি সম্পূর্ণ নতুন মেমোরি স্পেস নিয়ে কাজ করে, যা parent প্রক্রিয়ার একটি কপি।
Separate Memory Space:
Independent Execution:
নতুন প্রক্রিয়া তৈরি করা:
fork()
ফাংশন ব্যবহার করে Unix বা Linux সিস্টেমে নতুন প্রক্রিয়া তৈরি করা যায়। নতুন প্রক্রিয়া মূল প্রক্রিয়ার মতোই কিন্তু আলাদা মেমোরি স্পেসে কাজ করে।#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid;
// নতুন প্রক্রিয়া তৈরি করা
pid = fork();
if (pid < 0) {
// ত্রুটি হলে
perror("Fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child প্রক্রিয়ার কোড
printf("Child process: My PID is %d\n", getpid());
} else {
// Parent প্রক্রিয়ার কোড
printf("Parent process: My PID is %d\n", getpid());
printf("Child process ID: %d\n", pid);
}
return 0;
}
বৈশিষ্ট্য | Multi-threading | Forking |
---|---|---|
মেমোরি ব্যবহারের ধরন | Shared Memory ব্যবহার করে | Separate Memory ব্যবহার করে |
রিসোর্স ব্যবহারের পরিমাণ | কম রিসোর্স প্রয়োজন | বেশি রিসোর্স প্রয়োজন |
কর্মক্ষমতা | দ্রুত Context Switching | নতুন প্রক্রিয়া তৈরি করতে ধীর |
সিঙ্ক্রোনাইজেশন | থ্রেড সিঙ্ক্রোনাইজেশন প্রয়োজন | সিঙ্ক্রোনাইজেশন সাধারণত প্রয়োজন হয় না |
ব্যবহারিক উদাহরণ | নেটওয়ার্ক সার্ভার, রিয়েল-টাইম অ্যাপ্লিকেশন | পৃথক কাজ বা স্বাধীন প্রসেসের জন্য যেমন CLI Tools |
Parallel Execution | একই প্রক্রিয়ায় আলাদা কাজ সম্পন্ন করে | সম্পূর্ণ আলাদা প্রক্রিয়া তৈরি করে স্বতন্ত্রভাবে কাজ করে |
select()
, poll()
, এবং epoll()
হলো তিনটি গুরুত্বপূর্ণ সিস্টেম কল, যা নেটওয়ার্ক প্রোগ্রামিংয়ে I/O Multiplexing অর্জনের জন্য ব্যবহৃত হয়। এগুলোর মাধ্যমে একাধিক Socket বা File Descriptor-কে একসাথে পর্যবেক্ষণ করা যায় এবং এক বা একাধিক File Descriptor থেকে ইনপুট পাওয়া গেলে তা প্রক্রিয়া করা যায়। এগুলো বিশেষত নেটওয়ার্ক সার্ভার এবং উচ্চ-পারফরম্যান্স অ্যাপ্লিকেশন ডিজাইন করতে ব্যবহৃত হয়। নিচে প্রতিটি ফাংশনের ব্যবহার, প্রয়োজনীয়তা, এবং উদাহরণ দেওয়া হলো:
select()
select()
একটি পুরনো এবং সাধারণ সিস্টেম কল, যা File Descriptor সেটগুলোকে (Sockets, Files, Pipes ইত্যাদি) পর্যবেক্ষণ করে। এটি নির্দিষ্ট File Descriptor-এ I/O অপারেশন করার জন্য প্রস্তুত কিনা তা চেক করে।
select()
ফাংশনের সিগনেচার (C ভাষায়)int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds
: সর্বোচ্চ File Descriptor এর মান + 1।readfds
: File Descriptor সেট যা Read অপারেশনের জন্য পর্যবেক্ষণ করা হবে।writefds
: File Descriptor সেট যা Write অপারেশনের জন্য পর্যবেক্ষণ করা হবে।exceptfds
: File Descriptor সেট যা Exceptional Condition এর জন্য পর্যবেক্ষণ করা হবে।timeout
: কত সময় অপেক্ষা করবে (একটি টাইমআউট ভ্যালু)।select()
এর উদাহরণ#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <arpa/inet.h>
#define PORT 8080
int main() {
int server_fd, client_fd, max_fd, activity;
struct sockaddr_in server_addr;
fd_set read_fds;
char buffer[1024];
// Server socket তৈরি করা
server_fd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, 3);
while (1) {
FD_ZERO(&read_fds);
FD_SET(server_fd, &read_fds);
max_fd = server_fd;
// select() কল করা
activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
if (activity > 0 && FD_ISSET(server_fd, &read_fds)) {
client_fd = accept(server_fd, NULL, NULL);
printf("New connection accepted\n");
read(client_fd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(client_fd);
}
}
close(server_fd);
return 0;
}
poll()
poll()
হলো select()
-এর একটি উন্নত সংস্করণ, যা একাধিক File Descriptor-কে পর্যবেক্ষণ করে এবং I/O অপারেশন করার জন্য প্রস্তুত কিনা তা জানায়। এটি select()
-এর মতো কাজ করে, তবে এটি একটি Array ব্যবহার করে, যা select()
-এর Fixed-Size Set-এর চেয়ে বেশি নমনীয়।
poll()
ফাংশনের সিগনেচার (C ভাষায়)int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds
: struct pollfd
এর একটি Array যা File Descriptor এবং তাদের Event টাইপ ধারণ করে।nfds
: Array-তে থাকা File Descriptor এর সংখ্যা।timeout
: কতক্ষণ অপেক্ষা করবে (মিলিসেকেন্ডে)।poll()
এর উদাহরণ#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <arpa/inet.h>
#define PORT 8080
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr;
struct pollfd fds[10];
int nfds = 1;
char buffer[1024];
// Server socket তৈরি করা
server_fd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, 3);
fds[0].fd = server_fd;
fds[0].events = POLLIN;
while (1) {
int activity = poll(fds, nfds, -1);
if (activity > 0 && (fds[0].revents & POLLIN)) {
client_fd = accept(server_fd, NULL, NULL);
printf("New connection accepted\n");
read(client_fd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(client_fd);
}
}
close(server_fd);
return 0;
}
epoll()
epoll()
হলো Linux-এ select()
এবং poll()
এর আরও উন্নত সংস্করণ, যা উচ্চ-পারফরম্যান্স অ্যাপ্লিকেশন এবং সার্ভারের জন্য উপযুক্ত। এটি স্কেলেবিলিটির জন্য ডিজাইন করা হয়েছে এবং অনেক বেশি File Descriptor পরিচালনা করতে সক্ষম। epoll()
ব্যবহারের মাধ্যমে বেশিরভাগ ইভেন্ট-ড্রিভেন সার্ভার এবং অ্যাপ্লিকেশন তৈরি করা হয়।
epoll()
ফাংশনের তিনটি প্রধান অংশepoll_create()
:
epoll
instance তৈরি করে।int epoll_create(int size);
epoll_ctl()
:
epoll
instance-এ যোগ, সরানো বা পরিবর্তন করতে ব্যবহৃত হয়।int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_wait()
:
epoll
instance-এর মধ্যে থাকা File Descriptor গুলোতে ইভেন্ট আছে কিনা তা চেক করে এবং এটি ইভেন্ট পাওয়া গেলে তাদের ফেরত দেয়।int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll()
এর উদাহরণ#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#define PORT 8080
#define MAX_EVENTS 10
int main() {
int server_fd, client_fd, epoll_fd;
struct sockaddr_in server_addr;
struct epoll_event ev, events[MAX_EVENTS];
char buffer[1024];
// Server socket তৈরি করা
server_fd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, 3);
// epoll instance তৈরি করা
epoll_fd = epoll_create(1);
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
while (1) {
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < num_events; i++) {
if (events[i].data.fd == server_fd) {
client_fd = accept(server_fd, NULL, NULL);
printf("New connection accepted\n");
ev.events = EPOLLIN;
ev.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
} else {
int client_fd = events[i].data.fd;
read(client_fd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(client_fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);
}
}
}
close(server_fd);
close(epoll_fd);
return 0;
}
select()
, poll()
, এবং epoll()
বৈশিষ্ট্য | select() | poll() | epoll() |
---|---|---|---|
স্কেলেবিলিটি | সীমিত (1024 FD-এর বেশি পরিচালনা করতে পারে না) | উন্নত, কিন্তু select() এর মতো সমস্যা আছে | উচ্চ স্কেলেবিলিটি, অনেক বেশি FD পরিচালনা করতে পারে |
কর্মক্ষমতা | ধীর, কারণ এটি প্রতিটি কলের সময় সবকিছু স্ক্যান করে | তুলনামূলকভাবে ধীর | দ্রুত, কারণ এটি শুধুমাত্র Active FD-এর উপর কাজ করে |
ব্যবহারিকতা | ছোট অ্যাপ্লিকেশনের জন্য উপযুক্ত | মধ্যম স্কেল অ্যাপ্লিকেশনের জন্য | উচ্চ-পারফরম্যান্স এবং বড় অ্যাপ্লিকেশন এবং সার্ভারের জন্য |
পোর্টেবিলিটি | POSIX মান সম্মত, অনেক সিস্টেমে সমর্থিত | POSIX মান সম্মত, অনেক সিস্টেমে সমর্থিত | Linux নির্দিষ্ট |
Concurrent Server Design হলো এমন একটি সার্ভার আর্কিটেকচার, যেখানে সার্ভার একসাথে একাধিক ক্লায়েন্ট সংযোগ পরিচালনা করতে পারে। এটি নেটওয়ার্ক প্রোগ্রামিংয়ের একটি গুরুত্বপূর্ণ ধারণা এবং সাধারণত উচ্চ-পারফরম্যান্স ও স্কেলেবল সার্ভার ডিজাইন করার জন্য ব্যবহৃত হয়। Concurrent Server Design-এর মাধ্যমে সার্ভার একাধিক ক্লায়েন্টের জন্য দ্রুত সাড়া দিতে সক্ষম হয় এবং এর মাধ্যমে সার্ভারের কার্যকারিতা বৃদ্ধি করা যায়।
Concurrent Server Design-এ সাধারণত তিনটি পদ্ধতি ব্যবহার করা হয়:
Multiprocessing:
fork()
ফাংশন ব্যবহার করে নতুন প্রক্রিয়া তৈরি করা হয়।Multithreading:
Event-Driven/Asynchronous I/O:
select()
, poll()
, বা epoll()
-এর মতো I/O Multiplexing টেকনিক ব্যবহার করে।নিচে একটি TCP Concurrent Server প্রোগ্রাম দেখানো হলো, যা Multithreading পদ্ধতি ব্যবহার করে একাধিক ক্লায়েন্ট সংযোগ পরিচালনা করে:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
// ক্লায়েন্ট সংযোগ পরিচালনা করার ফাংশন
void *handle_client(void *client_socket) {
int sock = *(int*)client_socket;
char buffer[BUFFER_SIZE];
int n;
// ক্লায়েন্ট থেকে ডেটা গ্রহণ করা
while ((n = read(sock, buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("Client: %s\n", buffer);
// ক্লায়েন্টে ডেটা পাঠানো
write(sock, buffer, strlen(buffer));
}
// ক্লায়েন্টের সাথে সংযোগ বন্ধ করা
close(sock);
free(client_socket);
return NULL;
}
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// Server socket তৈরি করা
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// Socket binding করা
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// Listening শুরু করা
if (listen(server_fd, 5) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server is listening on port %d\n", PORT);
// ক্লায়েন্ট সংযোগ গ্রহণ করা
while ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) >= 0) {
printf("New client connected\n");
// প্রতিটি ক্লায়েন্টের জন্য নতুন থ্রেড তৈরি করা
pthread_t thread_id;
int *new_sock = malloc(sizeof(int));
*new_sock = client_fd;
if (pthread_create(&thread_id, NULL, handle_client, (void*)new_sock) != 0) {
perror("Thread creation failed");
close(client_fd);
}
// থ্রেড detach করা, যাতে সম্পন্ন হলে এটি নিজে থেকেই রিসোর্স মুক্ত করতে পারে
pthread_detach(thread_id);
}
// Server socket বন্ধ করা
close(server_fd);
return 0;
}
socket()
ফাংশন ব্যবহার করে TCP Socket তৈরি করা হয়েছে।bind()
ফাংশন ব্যবহার করে Socket-কে একটি নির্দিষ্ট IP Address এবং Port Number-এ সংযুক্ত করা হয়েছে।listen()
ফাংশন ব্যবহার করে সার্ভার Socket-কে ইনকামিং সংযোগ গ্রহণের জন্য প্রস্তুত করা হয়েছে।pthread_create()
ফাংশন ব্যবহার করে একটি নতুন থ্রেড তৈরি করা হয়েছে। এই থ্রেডগুলো handle_client()
ফাংশনের মাধ্যমে ক্লায়েন্টদের সাথে ডেটা আদান-প্রদান পরিচালনা করে।pthread_detach()
ফাংশন ব্যবহার করে থ্রেডগুলোকে detach করা হয়েছে, যাতে থ্রেডগুলো সম্পন্ন হলে তাদের রিসোর্স নিজে থেকেই মুক্ত হয়।read()
এবং write()
ফাংশন ব্যবহার করে ক্লায়েন্টের সাথে ডেটা পাঠানো এবং গ্রহণ করা হয়েছে।#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT 8080
#define BUFFER_SIZE 1024
void handle_client(int client_fd) {
char buffer[BUFFER_SIZE];
int n;
// ক্লায়েন্ট থেকে ডেটা গ্রহণ করা
while ((n = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("Client: %s\n", buffer);
// ক্লায়েন্টে ডেটা পাঠানো
write(client_fd, buffer, strlen(buffer));
}
close(client_fd);
}
void sigchld_handler(int s) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// Signal handler সেট করা
signal(SIGCHLD, sigchld_handler);
// Server socket তৈরি করা
server_fd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, 5);
printf("Server is listening on port %d\n", PORT);
// ক্লায়েন্ট সংযোগ গ্রহণ করা
while ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) >= 0) {
printf("New client connected\n");
if (fork() == 0) {
close(server_fd);
handle_client(client_fd);
exit(0);
}
close(client_fd);
}
close(server_fd);
return 0;
}
common.read_more